package cn.modfun.web.core.render;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.jfinal.kit.StrKit;
import com.jfinal.render.Render;
import com.jfinal.render.RenderException;

public class MyQrCodeRender extends Render {

	private String content;

	private int width = 300;

	private int height = 300;

	private ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.H;
	/* logo 来源 */
	private String logoSource = "file";

	private String logoPath = null;

	public MyQrCodeRender(String content){
		this.content = content == null ? "" : content;
	}
	
	public MyQrCodeRender(String content, ErrorCorrectionLevel errorCorrectionLevel) {
		this.content = content == null ? "" : content;
		this.errorCorrectionLevel = errorCorrectionLevel;
	}

	
	public MyQrCodeRender(String content, String logoSource, String logoPath,
			ErrorCorrectionLevel errorCorrectionLevel) {
		this.content = content == null ? "" : content;
		this.logoSource = logoSource;
		this.logoPath = logoPath;
		this.errorCorrectionLevel = errorCorrectionLevel;
	}

	public MyQrCodeRender(String content, String logoSource, String logoPath) {
		this.content = content == null ? "" : content;
		this.logoSource = logoSource;
		this.logoPath = logoPath;
	}

	public MyQrCodeRender(String content, String logoPath, ErrorCorrectionLevel errorCorrectionLevel) {
		this.content = content == null ? "" : content;
		this.logoPath = logoPath;
		this.errorCorrectionLevel = errorCorrectionLevel;
	}

	public MyQrCodeRender(String content, String logoPath) {
		this.content = content == null ? "" : content;
		this.logoPath = logoPath;
	}

	public MyQrCodeRender(String content, int width, int height, String logoSource, String logoPath,
			ErrorCorrectionLevel errorCorrectionLevel) {
		this.content = content == null ? "" : content;
		this.width = width;
		this.height = height;
		this.logoSource = logoSource;
		this.logoPath = logoPath;
		this.errorCorrectionLevel = errorCorrectionLevel;
	}

	public MyQrCodeRender(String content, int width, int height, String logoPath,
			ErrorCorrectionLevel errorCorrectionLevel) {
		this.content = content == null ? "" : content;
		this.width = width;
		this.height = height;
		this.logoPath = logoPath;
		this.errorCorrectionLevel = errorCorrectionLevel;
	}

	public MyQrCodeRender(String content, int width, int height, String logoSource, String logoPath) {
		this.content = content == null ? "" : content;
		this.width = width;
		this.height = height;
		this.logoSource = logoSource;
		this.logoPath = logoPath;
	}

	public MyQrCodeRender(String content, int width, int height, String logoPath) {
		this.content = content == null ? "" : content;
		this.width = width;
		this.height = height;
		this.logoPath = logoPath;
	}

	private BufferedImage createQrCodeBufferedImage(String content, int width, int height,
			Map<EncodeHintType, Object> hints) throws WriterException {
		if (StrKit.isBlank(content)) {
			throw new IllegalArgumentException("二维码内容不能为空");
		}
		MultiFormatWriter multiFormatWriter = null;
		BitMatrix bm = null;
		BufferedImage image = null;
		// 参数顺序分别为：编码内容，编码类型，生成图片宽度，生成图片高度，设置参数
		try {
			multiFormatWriter = new MultiFormatWriter();
			bm = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
			int w = bm.getWidth();
			int h = bm.getHeight();
			image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);

			// 开始利用二维码数据创建Bitmap图片，分别设为黑（0xFFFFFFFF）白（0xFF000000）两色
			for (int x = 0; x < w; x++) {
				for (int y = 0; y < h; y++) {
					image.setRGB(x, y, bm.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
				}
			}
		} catch (WriterException e) {
			throw e;
		}
		return image;
	}

	/**
	 * 混入logo
	 * 
	 * @param image
	 * @param is
	 * @param logoConfig
	 * @return
	 * @throws IOException
	 */
	private static BufferedImage mixedLogo(BufferedImage image, InputStream is, LogoConfig logoConfig)
			throws IOException {
		/**
		 * 读取二维码图片，并构建绘图对象
		 */
		Graphics2D g = image.createGraphics();
		// 计算图片放置位置
		int widthLogo = image.getWidth() / logoConfig.getLogoScale(),
				heightLogo = image.getHeight() / logoConfig.getLogoScale();
		int x = (image.getWidth() - widthLogo) / 2;
		int y = (image.getHeight() - heightLogo) / 2;
		BufferedImage logo = ImageIO.read(is);
		// 开始绘制图片
		g.drawImage(logo, x, y, widthLogo, heightLogo, null);// 绘制
		BasicStroke stroke = new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
		g.setStroke(stroke);// 设置笔画对象
		// 指定弧度的圆角矩形
		RoundRectangle2D.Float round = new RoundRectangle2D.Float(x, y, widthLogo, heightLogo, 20, 20);
		g.setColor(Color.white);
		g.draw(round);// 绘制圆弧矩形
		// 设置logo 有一道灰色边框
		BasicStroke stroke2 = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
		g.setStroke(stroke2);// 设置笔画对象
		RoundRectangle2D.Float round2 = new RoundRectangle2D.Float(x + 2, y + 2, widthLogo - 4, heightLogo - 4, 20, 20);
		g.setColor(new Color(128, 128, 128));
		g.draw(round2);// 绘制圆弧矩形
		g.dispose();
		logo.flush();
		image.flush();
		return image;
	}

	@Override
	public void render() {
		/**
		 * 
		 */
		response.setHeader("Pragma", "no-cache");
		response.setHeader("Cache-Control", "no-cache");
		response.setDateHeader("Expires", 0);
		response.setContentType("image/png");
		/**
		 * 
		 */
		Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
		hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
		hints.put(EncodeHintType.MARGIN, 0); // 去掉白色边框，极度重要，否则二维码周围的白边会很宽
		if (errorCorrectionLevel != null) {
			hints.put(EncodeHintType.ERROR_CORRECTION, errorCorrectionLevel);
		}

		BufferedImage image = null;
		try {
			image = createQrCodeBufferedImage(content, width, height, hints);
			/* 判断是否有logo */
			if (logoPath != null) {
				InputStream is = null;
				/* 判断logo来源 */
				if ("url".equals(logoSource)) {
					URL url = null;
					HttpURLConnection conn = null;
					try {
						url = new URL(logoPath);
						conn = (HttpURLConnection) url.openConnection();
						conn.setRequestMethod("GET");
						is = conn.getInputStream();
					} catch (MalformedURLException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}
				} else if ("file".equals(logoSource)) {
					File logoFile = new File(logoPath);
					if (logoFile.exists() && logoFile.isFile()) {
						try {
							is = new FileInputStream(logoFile);
						} catch (FileNotFoundException e) {
							e.printStackTrace();
						}
					}
				}
				if (is != null) {
					image = mixedLogo(image, is, new LogoConfig());
					is.close();
				}
			}
			ImageIO.write(image, "png", response.getOutputStream());
		} catch (Exception e) {
			throw new RenderException(e);
		}

	}

	public static class LogoConfig {
		/**
		 * 默认边框的颜色
		 */
		public static final Color DEFAULT_BORDER_COLOR = Color.WHITE;
		/**
		 * 默认边框的宽度
		 */
		public static final int DEFAULT_BORDER_SIZE = 2;
		/**
		 * 默认logo相对于二维码的比例
		 */
		public static final int DEFAULT_LOGO_SCALE = 5;

		/**
		 * 实际用的边框值
		 */
		private int borderSize = DEFAULT_BORDER_SIZE;
		/**
		 * 实际用的边框颜色
		 */
		private Color borderColor = DEFAULT_BORDER_COLOR;
		/**
		 * 实际用的logo相对二维码的比例
		 */
		private int logoScale = DEFAULT_LOGO_SCALE;

		public LogoConfig() {
		}

		public LogoConfig(Color borderColor, int logoScale) {
			this.borderColor = borderColor;
			this.logoScale = logoScale;
		}

		public LogoConfig(int borderSize, Color borderColor, int logoScale) {
			this.borderSize = borderSize;
			this.borderColor = borderColor;
			this.logoScale = logoScale;
		}

		public int getBorderSize() {
			return borderSize;
		}

		public void setBorderSize(int borderSize) {
			this.borderSize = borderSize;
		}

		public Color getBorderColor() {
			return borderColor;
		}

		public void setBorderColor(Color borderColor) {
			this.borderColor = borderColor;
		}

		public int getLogoScale() {
			return logoScale;
		}

		public void setLogoScale(int logoScale) {
			this.logoScale = logoScale;
		}

	}
}
